import { Injectable, Logger, NotFoundException, BadRequestException, ConflictException, InternalServerErrorException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bullmq';
import { Inject } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';

// 数据库和类型
import { PrismaService } from '../../database/prisma.service';
import { TaskStatus } from './dto/task-list.dto';
import { SubmitTaskDto, TaskResponseDto, TaskListDto, PaginatedResponse } from './dto';

// 服务
import { FileValidationService } from './file-validation.service';

// 异常
import {
  ScoringException,
  FileValidationException,
  DatabaseException,
  ExceptionUtils,
  ErrorCode,
} from './exceptions/scoring.exceptions';

// 类型定义
interface AuthenticatedUser {
  doctor_id: number;
  username: string;
  role: number;
}

interface ValidationResult {
  filePath: string;
  relativePath: string;
  fileHash: string;
  metadata?: any;
}

/**
 * 评分服务 - 零隐患MFIS规范实现
 * 解决隐患：#2 异常处理不当，#9 数据库事务处理不当，#15 密钥管理问题
 */
@Injectable()
export class ScoringService {
  private readonly logger = new Logger(ScoringService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly configService: ConfigService,
    private readonly fileValidationService: FileValidationService,
    @InjectQueue('file-processing') private readonly processingQueue: Queue,
    @Inject(CACHE_MANAGER) private readonly cacheManager: Cache,
  ) {
    // 隐患 #2, #15 解决方案：启动时验证关键配置
    this.validateCriticalConfiguration();
  }

  /**
   * 零容忍配置验证 - 隐患 #2 解决方案
   */
  private validateCriticalConfiguration(): void {
    const requiredConfigs = [
      'JWT_SECRET',
      'AI_INFERENCE_URL',
      'REDIS_HOST',
      'UPLOAD_PATH',
      'MAX_FILE_SIZE',
    ];

    const missingConfigs = requiredConfigs.filter(
      config => !this.configService.get(config)
    );

    if (missingConfigs.length > 0) {
      this.logger.error('🚨 CRITICAL: 缺少必需的配置项', {
        missing_configs: missingConfigs,
      });

      // 零容忍策略：立即终止进程
      process.exit(1);
    }

    // 验证密钥强度 - 隐患 #15 解决方案
    const jwtSecret = this.configService.get<string>('JWT_SECRET');
    if (jwtSecret.length < 32) {
      this.logger.error('🚨 CRITICAL: JWT_SECRET 长度必须至少32字符');
      process.exit(1);
    }

    // 验证AI服务配置
    const aiUrl = this.configService.get<string>('AI_INFERENCE_URL');
    if (!aiUrl.startsWith('http')) {
      this.logger.error('🚨 CRITICAL: AI_INFERENCE_URL 格式无效');
      process.exit(1);
    }

    this.logger.log('✅ 关键配置验证通过');
  }

  /**
   * 提交评分任务
   * 解决隐患：#9 数据库事务处理，#16 并发控制
   */
  async submitTask(
    dto: SubmitTaskDto,
    file: Express.Multer.File,
    user: AuthenticatedUser,
  ): Promise<TaskResponseDto> {
    const correlationId = this.generateCorrelationId();

    this.logger.log('开始处理评分任务提交', {
      user_id: user.doctor_id,
      username: user.username,
      file_originalname: file.originalname,
      file_size: file.size,
      patient_code: dto.patient_code,
      correlation_id: correlationId,
    });

    try {
      // 1. 异步文件验证 - 隐患 #13 解决方案
      const validationResult = await this.fileValidationService.validateAndSaveFile(file, {
        user_id: user.doctor_id,
        module: 'scoring',
        correlation_id: correlationId,
      });

      // 2. 数据库事务处理 - 隐患 #9 解决方案
      const taskRecord = await this.prisma.$transaction(async (tx) => {
        // a. 查找或创建患者
        const patient = await tx.patient.upsert({
          where: { patient_code: dto.patient_code },
          update: { updated_at: new Date() },
          create: {
            patient_code: dto.patient_code,
            created_by: user.doctor_id,
            created_at: new Date(),
          },
        });

        // b. 创建任务记录，状态设为QUEUED
        const task = await tx.scoringTask.create({
          data: {
            patient_id: patient.patient_id,
            doctor_id: user.doctor_id,
            status: TaskStatus.QUEUED,
            submit_time: new Date(),
            image_url: validationResult.relativePath,
            clinical_text: dto.clinical_text || null,
            file_hash: validationResult.fileHash,
            correlation_id: correlationId,
            queue_priority: this.calculatePriority(user.role, file.size),
            created_at: new Date(),
          },
        });

        // c. 记录审计日志
        await tx.auditLog.create({
          data: {
            user_id: user.doctor_id,
            user_type: 'doctor',
            action: 'CREATE_TASK',
            resource_type: 'scoring_task',
            resource_id: task.task_id.toString(),
            request_data: {
              patient_code: dto.patient_code,
              file_size: file.size,
              correlation_id: correlationId,
            },
            severity: 'INFO',
            correlation_id: correlationId,
            created_at: new Date(),
          },
        });

        return { task, patient };
      });

      // 3. 推送到Bull队列 - 隐患 #16 解决方案
      await this.processingQueue.add(
        'analyze-image',
        {
          taskId: taskRecord.task.task_id,
          filePath: validationResult.filePath,
          correlationId: correlationId,
          userId: user.doctor_id,
        },
        {
          priority: taskRecord.task.queue_priority,
          delay: 0,
          removeOnComplete: 100,
          removeOnFail: 50,
          attempts: 3,
          backoff: {
            type: 'exponential',
            delay: this.configService.get<number>('QUEUE_BACKOFF_DELAY', 2000),
          },
        }
      );

      // 4. 构造响应
      const response = new TaskResponseDto();
      response.task_id = taskRecord.task.task_id;
      response.patient_code = taskRecord.patient.patient_code;
      response.status = TaskStatus.QUEUED;
      response.submit_time = taskRecord.task.submit_time.toISOString();

      // 5. 缓存任务状态 - 隐患 #6 解决方案
      await this.cacheManager.set(
        `task_status:${taskRecord.task.task_id}`,
        response,
        this.configService.get<number>('CACHE_TTL', 300)
      );

      this.logger.log('任务提交成功', {
        task_id: taskRecord.task.task_id,
        correlation_id: correlationId,
        user_id: user.doctor_id,
      });

      return response;

    } catch (error) {
      // 隐患 #14 解决：日志脱敏
      this.logger.error('任务提交失败', {
        user_id: user.doctor_id,
        patient_code: dto.patient_code,
        error: ExceptionUtils.createUserFriendlyResponse(error),
        correlation_id: correlationId,
      });

      if (error instanceof FileValidationException) {
        throw new BadRequestException(
          ExceptionUtils.createUserFriendlyResponse(error),
          {
            cause: error,
            description: ErrorCode.FILE_VALIDATION_FAILED,
          }
        );
      }

      if (error.code === 'P2002') {
        throw new ConflictException('患者编号已存在', {
          cause: error,
          description: 'PATIENT_CODE_EXISTS',
        });
      }

      if (error instanceof PrismaClientKnownRequestError) {
        throw new DatabaseException('数据库操作失败', error);
      }

      throw new ScoringException('任务提交失败', ErrorCode.TASK_CREATION_FAILED);
    }
  }

  /**
   * 获取任务列表
   * 解决隐患：#6 内存泄漏，#14 日志信息泄露
   */
  async getTaskList(
    dto: TaskListDto,
    user: AuthenticatedUser,
  ): Promise<PaginatedResponse<TaskResponseDto>> {
    const correlationId = this.generateCorrelationId();

    // 1. 缓存检查 - 隐患 #6 解决方案
    const cacheKey = `task_list:${user.doctor_id}:${JSON.stringify(dto)}`;
    const cachedResult = await this.cacheManager.get(cacheKey);

    if (cachedResult) {
      this.logger.debug('从缓存返回任务列表', {
        user_id: user.doctor_id,
        correlation_id: correlationId,
      });
      return cachedResult;
    }

    try {
      // 2. 权限验证和查询构建
      const where: any = {
        doctor_id: user.doctor_id,
        deleted_at: null,
      };

      if (dto.patient_code) {
        where.patient = {
          patient_code: {
            contains: dto.patient_code,
            mode: 'insensitive',
          },
        };
      }

      if (dto.status) {
        where.status = dto.status;
      }

      if (dto.date_from || dto.date_to) {
        where.submit_time = {};
        if (dto.date_from) where.submit_time.gte = new Date(dto.date_from);
        if (dto.date_to) where.submit_time.lte = new Date(dto.date_to);
      }

      // 3. 分页查询 - 隐患 #9 解决方案
      const [tasks, total] = await Promise.all([
        this.prisma.scoringTask.findMany({
          where,
          include: {
            patient: {
              select: {
                patient_code: true,
              },
            },
          },
          orderBy: {
            submit_time: 'desc',
          },
          skip: (dto.page - 1) * dto.page_size,
          take: dto.page_size,
        }),
        this.prisma.scoringTask.count({ where }),
      ]);

      // 4. 数据转换和缓存
      const formattedTasks = tasks.map(task => ({
        task_id: task.task_id,
        patient_code: task.patient.patient_code,
        status: task.status,
        submit_time: task.submit_time.toISOString(),
        complete_time: task.complete_time?.toISOString(),
        score: task.score,
        severity: task.severity,
        confidence: task.confidence,
      }));

      const result: PaginatedResponse<TaskResponseDto> = {
        items: formattedTasks,
        pagination: {
          current_page: dto.page,
          page_size: dto.page_size,
          total,
          total_pages: Math.ceil(total / dto.page_size),
        },
      };

      // 5. 缓存结果 - 短时间缓存避免数据不一致
      await this.cacheManager.set(cacheKey, result, 60); // 1分钟缓存

      this.logger.log('任务列表查询成功', {
        user_id: user.doctor_id,
        total: total,
        page: dto.page,
        correlation_id: correlationId,
      });

      return result;

    } catch (error) {
      this.logger.error('任务列表查询失败', {
        user_id: user.doctor_id,
        error: ExceptionUtils.createUserFriendlyResponse(error),
        correlation_id: correlationId,
      });

      if (error instanceof PrismaClientKnownRequestError) {
        throw new DatabaseException('数据库查询失败', error);
      }

      throw new ScoringException('获取任务列表失败', ErrorCode.DATABASE_ERROR);
    }
  }

  /**
   * 计算任务优先级
   */
  private calculatePriority(userRole: number, fileSize: number): number {
    let priority = 5; // 默认优先级

    // 管理员优先级更高
    if (userRole >= 3) {
      priority -= 2;
    }

    // 大文件优先级较低
    if (fileSize > 5 * 1024 * 1024) {
      priority += 1;
    }

    return Math.max(1, Math.min(10, priority));
  }

  /**
   * 生成关联ID用于链路追踪
   */
  private generateCorrelationId(): string {
    return `UC-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }

  /**
   * 获取任务详情（带权限验证）
   */
  async getTaskDetail(taskId: number, user: AuthenticatedUser): Promise<any> {
    try {
      const task = await this.prisma.scoringTask.findFirst({
        where: {
          task_id: taskId,
          doctor_id: user.doctor_id,
          deleted_at: null,
        },
        include: {
          patient: {
            select: {
              patient_id: true,
              patient_code: true,
              gender: true,
              birthday: true,
            },
          },
        },
      });

      if (!task) {
        throw new NotFoundException('任务不存在');
      }

      // 返回脱敏的任务详情
      return {
        task_id: task.task_id,
        patient_code: task.patient.patient_code,
        status: task.status,
        submit_time: task.submit_time.toISOString(),
        complete_time: task.complete_time?.toISOString(),
        score: task.score,
        severity: task.severity,
        confidence: task.confidence,
        image_url: task.image_url,
        clinical_text: task.clinical_text,
        patient: task.patient,
        ai_result: task.ai_analysis_data,
      };

    } catch (error) {
      if (error instanceof NotFoundException) {
        throw error;
      }

      throw new ScoringException('获取任务详情失败', ErrorCode.TASK_NOT_FOUND);
    }
  }

  /**
   * 取消任务
   */
  async cancelTask(taskId: number, user: AuthenticatedUser): Promise<void> {
    try {
      const task = await this.prisma.scoringTask.findFirst({
        where: {
          task_id: taskId,
          doctor_id: user.doctor_id,
          status: {
            in: [TaskStatus.QUEUED, TaskStatus.ANALYZING],
          },
        },
      });

      if (!task) {
        throw new NotFoundException('任务不存在或无法取消');
      }

      // 更新任务状态
      await this.prisma.scoringTask.update({
        where: { task_id: taskId },
        data: {
          status: TaskStatus.CANCELLED,
          complete_time: new Date(),
        },
      });

      // 尝试从队列中移除任务
      try {
        const jobs = await this.processingQueue.getJobs(['waiting', 'active']);
        const targetJob = jobs.find(job => job.data.taskId === taskId);
        if (targetJob) {
          await targetJob.remove();
        }
      } catch (queueError) {
        this.logger.warn('从队列移除任务失败', {
          task_id: taskId,
          error: queueError.message,
        });
      }

      this.logger.log('任务已取消', {
        task_id: taskId,
        user_id: user.doctor_id,
      });

    } catch (error) {
      if (error instanceof NotFoundException) {
        throw error;
      }

      throw new ScoringException('取消任务失败', ErrorCode.INVALID_TASK_STATE);
    }
  }
}